﻿//-----------------------------------------------------------------------
// <copyright file="Throbber.xaml.cs" company="AFS">
// Copyright (c) AFS. All rights reserved.
//
//    This file is part of Chatbox.
//
//    Chatbox is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Lesser General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    any later version.
//
//    Chatbox is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with Chatbox. If not, see http://www.gnu.org/copyleft/lesser.html
// </copyright>
//----------------------------------------------------------------------- 

namespace ChatBox.UI.Controls
{
    using System;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Threading;

    using ChatBox.DomainObjects;
    using ChatBox.XamlUtility;

    /// <summary>
    /// Throbber control shows connection state
    /// </summary>
    public partial class Throbber : UserControl
    {
        /// <summary>
        /// The brushes used
        /// </summary>
        private readonly Brush[] brushes = new Brush[5];

        /// <summary>
        /// Timer for ticking over into idle state
        /// </summary>
        private readonly DispatcherTimer timer;

        /// <summary>
        /// How long before the connection is idle
        /// </summary>
        private readonly TimeSpan idleInterval = new TimeSpan(0, 1, 0);

        /// <summary>
        /// the last time that the "Ok with data" state was received
        /// </summary>
        private DateTime lastDataReceived;

        /// <summary>
        /// State of user log on
        /// </summary>
        private ConnectionState logOnState;

        /// <summary>
        /// Initializes a new instance of the Throbber class
        /// </summary>
        public Throbber()
        {
            this.InitializeComponent();

            this.ConnectionState = ConnectionState.NotConnected;
            this.LogOnState = ConnectionState.NotConnected;

            // the timer calls back once every 2 seconds to check for transition to idle state
            this.timer = new DispatcherTimer(DispatcherPriority.Normal, this.Dispatcher);
            this.timer.Interval = new TimeSpan(0, 0, 2);
            this.timer.Tick += this.TimerTick;
            this.timer.Start();
        }

        /// <summary>
        /// Gets the state of the throbber
        /// </summary>
        public ConnectionState ConnectionState { get; private set; }

        /// <summary>
        /// Gets or sets the log-on state of the user
        /// </summary>
        public ConnectionState LogOnState
        {
            get
            {
                return this.logOnState;
            }

            set
            {
                this.logOnState = value;
                loggedOnElipse.Fill = this.GetBrush(value);
            }
        }

        /// <summary>
        /// Do the throb animation
        /// </summary>
        /// <param name="state">the new state</param>
        public void Throb(ConnectionState state)
        {
            if (state == ConnectionState.DataReceived)
            {
                this.lastDataReceived = DateTime.Now;
            }

            this.ConnectionState = state;

            throbberElipse.Fill = this.GetBrush(state);
            throbberBackElipse.Fill = throbberElipse.Fill;

            // animate it
            Storyboard throbAnimation = this.FindStoryboard("throbStoryboard");
            throbAnimation.Begin();
        }

        /// <summary>
        /// Get the brush, create the brush as it is needed
        /// </summary>
        /// <param name="state">the state for the brush</param>
        /// <returns>the brush to use</returns>
        private Brush GetBrush(ConnectionState state)
        {
            int index = (int)state;

            if (this.brushes[index] == null)
            {
                Brush newBrush;

                switch (state)
                {
                    case ConnectionState.Unknown:
                        newBrush = new SolidColorBrush(Colors.Brown);
                        break;

                    case ConnectionState.NotConnected:
                        newBrush = new SolidColorBrush(Colors.Black);
                        break;

                    case ConnectionState.Idle:
                        newBrush = new SolidColorBrush(Colors.DarkGreen);
                        break;

                    case ConnectionState.DataReceived:
                        newBrush = new SolidColorBrush(Colors.LimeGreen);
                        break;

                    case ConnectionState.Error:
                        newBrush = new SolidColorBrush(Colors.Red);
                        break;

                    default:
                        throw new ChatBoxException("No brush found for state" + index);
                }

                this.brushes[index] = newBrush; 
            }

            return this.brushes[index];
        }

        /// <summary>
        /// Timer ticked event handler
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the event params</param>
        private void TimerTick(object sender, EventArgs e)
        {
            // transation to "Idle" if we're in the OK state and sufficient time has passed
            if ((this.ConnectionState == ConnectionState.DataReceived) && (DateTime.Now - this.lastDataReceived > this.idleInterval))
            {
                this.ConnectionState = ConnectionState.Idle;

                // this State transition is not animated. 
                // The animation is a visual cue that something has happened, and going Idle is the opposite of that
                throbberElipse.Fill = this.GetBrush(this.ConnectionState);
                throbberBackElipse.Fill = throbberElipse.Fill;
            }
        }
    }
}